﻿using Microsoft.EntityFrameworkCore;
using Project.IRepository;
using Project.Models;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Project.Repository
{
    public class BaseRepository: IBaseRepository
    {
        private readonly ProjectDbContext context;

        public BaseRepository(ProjectDbContext context)
        {
            this.context = context;
        }

        public void Add<T>(T entity) where T : BaseModel
        {
            entity.Id = Guid.NewGuid();
            entity.CreateAt = DateTime.Now;
            context.Set<T>().Add(entity);
        }

        public void Delete<T>(T entity) where T : BaseModel
        {
            context.Entry<T>(entity).State = Microsoft.EntityFrameworkCore.EntityState.Deleted;
        }

        public void Delete<T>(Expression<Func<T, bool>> expression) where T : BaseModel
        {
            var objs = context.Set<T>().Where(expression);
            foreach (var o in objs)
            {
                context.Set<T>().Remove(o);
            }
        }

        public void Delete<T>(Guid key) where T : BaseModel, new()
        {
            T obj = new T() { Id = key };
            context.Entry<T>(obj).State = EntityState.Deleted;
        }

        public DataTable ExecuteDataTable(string sql, params IDataParameter[] ps)
        {
            using (var conn = context.Database.GetDbConnection())
            {
                var cmd = conn.CreateCommand();
                cmd.CommandText = sql;
                if (ps != null)
                    cmd.Parameters.AddRange(ps);
                var table = new DataTable();
                var reader = cmd.ExecuteReader();
                table.Load(reader);
                reader.Close();
                return table;
            }
        }

        public object ExecuteScalar(string sql, params IDataParameter[] ps)
        {
            using (var conn = context.Database.GetDbConnection())
            {
                var cmd = conn.CreateCommand();
                cmd.CommandText = sql;
                if (ps != null)
                    cmd.Parameters.AddRange(ps);
                conn.Open();
                return cmd.ExecuteScalar();
            }
        }

        public int ExecuteNonQuery(string sql, params IDataParameter[] ps)
        {
            using (var conn = context.Database.GetDbConnection())
            {
                var cmd = conn.CreateCommand();
                cmd.CommandText = sql;
                if (ps != null)
                    cmd.Parameters.AddRange(ps);
                conn.Open();
                return cmd.ExecuteNonQuery();
            }

        }

        public async Task<bool> ExistsAsync<T>(Expression<Func<T, bool>> expression) where T : BaseModel
        {
            return await context.Set<T>().AsNoTracking().Where(o=>o.IsDeleted==false).AnyAsync(expression);
        }

        public T Find<T>(Guid key) where T : BaseModel
        {
            return context.Set<T>().AsNoTracking().FirstOrDefault(n => n.Id == key && n.IsDeleted==false);
        }

        public async Task<T> FindAsync<T>(Guid key) where T : BaseModel
        {
            return await context.Set<T>().AsNoTracking().FirstOrDefaultAsync(n => n.Id == key && n.IsDeleted == false);
        }

        public IQueryable<T> Find<T>() where T : BaseModel
        {
            return context.Set<T>().Where(t => t.IsDeleted == false).AsNoTracking();
        }

        public IQueryable<T> Find<T>(Expression<Func<T, bool>> expression) where T : BaseModel
        {
            return context.Set<T>().Where(expression).Where(t => t.IsDeleted == false).AsNoTracking();
        }

        public IQueryable<T> Find<T, TOrder>(Expression<Func<T, bool>> expression, Expression<Func<T, TOrder>> order, bool isAsc = true) where T : BaseModel
        {
            if (isAsc)
                return context.Set<T>().Where(expression).Where(t => t.IsDeleted == false).OrderBy(order).AsNoTracking();
            else
                return context.Set<T>().Where(expression).Where(t => t.IsDeleted == false).OrderByDescending(order).AsNoTracking();
        }

        //public IQueryable<T> Find<T, TOrder>(int pageIndex, int pageSize, out int count, Expression<Func<T, bool>> expression = null, Expression<Func<T, TOrder>> order = null, bool isAsc = true) where T : BaseModel
        //{
        //    IQueryable<T> query = context.Set<T>().Where(t => t.IsDeleted == false).AsNoTracking();
        //    if (expression == null)
        //    {
        //        count = context.Set<T>().Where(t => t.IsDeleted == false).Count();
        //    }
        //    else
        //    {
        //        count = context.Set<T>().Where(expression).Where(t => t.IsDeleted == false).Count();
        //        query = context.Set<T>().Where(expression).Where(t => t.IsDeleted == false);
        //    }


        //    if (isAsc)
        //    {
        //        if (order == null)
        //            query = query.OrderBy(o => o.Id);
        //        else
        //            query = query.OrderBy(order);

        //    }
        //    else
        //    {
        //        if (order == null)
        //            query = query.OrderByDescending(o => o.Id);
        //        else
        //            query = query.OrderByDescending(order);
        //    }
        //    query = query.Skip(pageSize * (pageIndex - 1)).Take(pageSize);

        //    return query.AsNoTracking();
        //}

        public void Update<T>(T entity, params string[] updateFields) where T : BaseModel
        {
            var entry = context.Entry<T>(entity);
            //entry.State = EntityState.Modified;
            if (updateFields.Length > 0)
            {
                foreach (var col in updateFields)
                {
                    entry.Property(col).IsModified = true;
                }
            }
            else
            {
                entry.State = EntityState.Modified;
            }
        }




        public IQueryable<T> Find<T, TOrder>(int pageIndex, int pageSize, Expression<Func<T, bool>> expression, Expression<Func<T, TOrder>> order, out int count, bool isAsc = true) where T : BaseModel
        {
            IQueryable<T> query = context.Set<T>().Where(t => t.IsDeleted == false);
            if (expression == null)
            {
                count = query.Count();
            }
            else
            {
                count = query.Where(expression).Count();
                query = query.Where(expression);
            }


            if (isAsc)
            {
                if (order == null)
                    query = query.OrderBy(o => o.Id);
                else
                    query = query.OrderBy(order);

            }
            else
            {
                if (order == null)
                    query = query.OrderByDescending(o => o.Id);
                else
                    query = query.OrderByDescending(order);
            }
            query = query.Skip(pageSize * (pageIndex - 1)).Take(pageSize);

            return query.AsNoTracking();
        }
    }
}
